#include <iostream>
#include <curl/curl.h>
#include <curl/easy.h>
#include <boost/thread/mutex.hpp> 
#include <boost/shared_ptr.hpp>
#include <boost/format.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp> 

#include <mysql++/mysql++.h>
#include <mysql++/ssqls.h>

#include "CosDb.h"
#include "trace_worker.h"
#include "ClientManager.h"
#include "Md5.h"
#include "tinyxml2.h"
#include "JsonHelper.h"
#include "IweChenCfg.h"

using namespace boost::posix_time;
using namespace boost::gregorian;
using namespace qcloud_cos;


static boost::mutex g_insMutexCalc;


CCosDb* CCosDb::_instance = NULL;

CCosDb* CCosDb::instance() 
{	
	if (NULL == _instance)
	{
		boost::unique_lock<boost::mutex> guardMutex(g_insMutexCalc);
		if (NULL == _instance)
		{
			_instance = new CCosDb;
		}
	}
	return _instance;
}

CCosDb::CCosDb()
:m_cosCfg("cos.json")
,m_cos(m_cosCfg)
,m_sqlConnPool(*CSqlConnPool::instance())
,m_redisConnPool(*CRedisConnPool::instance())
{
    const rapidjson::Value &sessionValue = CWeChenCfg::instance()->Cfg()["SESSION"];
    m_qrcodeAurl = sessionValue["qrcode_aurl"].GetString();
    m_infoPage = sessionValue["info_page"].GetString();
}

CCosDb::~CCosDb()
{}

bool CCosDb::GetAccessToken(rapidjson::Document &document, const std::string &appId, std::string &accessToken)
{   trace_worker();
    rapidjson::Value getAccessTokenValue(rapidjson::kObjectType);    
    getAccessTokenValue.AddMember("appId", rapidjson::Value(appId.c_str(), document.GetAllocator()), document.GetAllocator());
    getAccessTokenValue.AddMember("openId", "openId", document.GetAllocator());

    rapidjson::Document tmpDocument;
    if (!CClientManager::instance()->thriftMethodSyn(tmpDocument, "getAccessToken", getAccessTokenValue, document)
        || !tmpDocument.HasMember("content")
        || !tmpDocument["content"].IsString())
    {
        return false;
    }

    accessToken = tmpDocument["content"].GetString();
    return true;
}

bool CCosDb::GetInfoQRCode(rapidjson::Document &document, const std::string &appId, const std::string &pubInfoId, std::string &infoQRCode)
{   trace_worker();
    SynInfoQRCode(document, appId, pubInfoId);
    return RedisToInfoQRCode(pubInfoId, infoQRCode);
}

bool CCosDb::SynInfoQRCode(rapidjson::Document &document, const std::string &appId, const std::string &pubInfoId)
{   trace_worker();
    if (m_redisConnPool.IsRedisKeyExists(pubInfoId + ":infoQRCode") == true)
    {   trace_printf("true");
        return true;
    }

    std::string infoQRCode;
    if (UrlToInfoQRCode(document, appId, pubInfoId, infoQRCode) == false)
    {
        return false;
    }

    return InfoQRCodeToRedis(pubInfoId, infoQRCode);
}

bool CCosDb::RedisToInfoQRCode(const std::string &pubInfoId, std::string &infoQRCode)
{   trace_worker();
    boost::shared_ptr<CRedisClusterContext> redisConn;
    if (m_redisConnPool.GetConn(redisConn))
    {
        CConnGuard<CRedisConnPool, boost::shared_ptr<CRedisClusterContext>> connGuard(m_redisConnPool, redisConn);
        boost::shared_ptr<CRedisReply> reply = redisConn->redisClusterCommand("GET %s:infoQRCode", pubInfoId.c_str());
        if (reply && reply->type == REDIS_REPLY_STRING && reply->str != NULL)
        {            
            trace_printf("reply->str  %s", reply->str);
            infoQRCode = reply->str;
            return true;
        }

    }
    return false;
}

bool CCosDb::InfoQRCodeToRedis(const std::string &pubInfoId, const std::string &infoQRCode)
{   trace_worker();
    boost::shared_ptr<CRedisClusterContext> redisConn;
    if (m_redisConnPool.GetConn(redisConn))
    {
        CConnGuard<CRedisConnPool, boost::shared_ptr<CRedisClusterContext>> connGuard(m_redisConnPool, redisConn);
        redisConn->redisClusterCommand("SET %s:infoQRCode %s", pubInfoId.c_str(), infoQRCode.c_str());
        trace_printf("SET %s:infoQRCode %s", pubInfoId.c_str(), infoQRCode.c_str());
    }
    return true;
}

bool CCosDb::UrlToInfoQRCode(rapidjson::Document &document, const std::string &appId, const std::string &pubInfoId, std::string &infoQRCode)
{   trace_worker();
    std::string accessToken;
    if (GetAccessToken(document, appId, accessToken) == false)
    {
        return false;
    }
    trace_printf("accessToken.c_str()  %s", accessToken.c_str());
    CHttpClient httpClient;
    std::string qrcodeAurl = (boost::format("%s?access_token=%s") % m_qrcodeAurl.c_str() % accessToken.c_str()).str();
    std::string postResponse;
    std::string postReq = (boost::format("{\"scene\":\"%s\", \"page\": \"%s\"}") % pubInfoId.c_str() % m_infoPage.c_str()).str();
    trace_printf("postReq.c_str()  %s", postReq.c_str());
    httpClient.Post(qrcodeAurl, postReq, postResponse);
    trace_printf("postResponse.size()  %d", postResponse.size());

    std::size_t folderPos = pubInfoId.find_last_of(':');
    if (folderPos == std::string::npos)
    {   trace_printf("pubInfoId.c_str()  %s", pubInfoId.c_str());
        return false;
    } 

    string folder = "/" + pubInfoId.substr(0, folderPos);
    string cosPath = folder + "/" + pubInfoId.substr(folderPos + 1) + ".png";

    string bucket = "infoqr-code";
    string folder_biz_attr = "folder attribute";
    FolderCreateReq folderCreateReq(bucket, folder,folder_biz_attr);
    m_cos.FolderCreate(folderCreateReq);

    FileUploadReq fileUploadReq(bucket,cosPath, postResponse.c_str(), postResponse.size());
    fileUploadReq.setInsertOnly(0);
    std::string result = m_cos.FileUpload(fileUploadReq);
    trace_printf("result.c_str()  %s", result.c_str());
    rapidjson::Document tmpDocument;
    if (tmpDocument.Parse(result.c_str()).HasParseError()
        || !tmpDocument.HasMember("data")
        || !tmpDocument["data"].HasMember("source_url"))
    {
        return false;
    }
    infoQRCode = tmpDocument["data"]["source_url"].GetString();
    trace_printf("infoQRCode.c_str()  %s", infoQRCode.c_str());
    return true;
}



